Getting parent header number

I need to have child objects be assigned an attribute that contains the child's parent's header number. For example:

3.1 Requirements (Parent Object)
-The car shall do X. (Child Object)

In this example the child object would have a value of 3.1 for the attribute value in question.

Thanks in advance for any help.
cbabcock - Tue Jan 10 13:20:40 EST 2012

Re: Getting parent header number
llandale - Tue Jan 10 15:44:52 EST 2012

WARNING: I just realized the attr-DXL solution below fails to take into account:

  • You do not want to display the paragraph number of a deleted object, and
  • You do not want to display the paragraph number for a deleted object, and
  • It fails when the module is currently displaying deleted objects, since the "number" perm displays differently and you cannot turn on and off that display in an attr-DXL or layout.


Seems then perhaps you need an on-demand script that keeps some target attr value up to date, or some wierd post-module-open trigger that does this work. Or open modules loading the standard view and use the attr-dxl below.

 

Use Attr-DXL.

It seems the requirement is that it will display the object's own paragraph number if its a heading, or if its a text object then display the nearest ancestor's Heading. Thus in your example, the attr-DXL will display "3.1" for both your objects. Perhaps the name of the target attribute is "dxlSectionNumber".

This may work. The key function works well but I'm scribbling the rest.

 

 

//**************************
bool    fIsTable(Object obj)
{     // Is the object part of a DOORS table?
        if (null obj) return(false)
 
        return (row(obj) or table(obj) or cell(obj))
}     // end fIsTable()
 
                // Following buffer is local to fIsHead()
Buffer  gl_bufIsHeadNum = create(32)
//***********************
bool    fIsHead(Object obj)
{     // Does DOORS consider the object a 'Heading' object?
        // Heading Objects are those that display the paragraph number in the Main column in DOORS
        // DOORS considerations for determining 'Heading Objects':
        //      [a] Objects that contain some non-null rich Heading text.
        //      [b] Objects that have no non-null rich Object Text and no Pictures (i.e. empty objects)
        //      [c] However, Cell, row header, and table header objects are NEVER headings,
        //          even when they contain Object Heading or are empty.
        //      [d] Note that invisible rich-text markup only is considered.
        // This function looks at the Paragraph number of the object to determine if
        //      DOORS considers it a Heading.
 
        // Notes:
        // [1]  Normal Text Object Paragraph Numbers have a ZeroDash and look like this: "3.2.1.0-4".
        //      Notice there is a dash after the last period.
        // [2]  Normal Headings Numbers have no dash and look like: "3.2.1".
        // [3]  When a Heading is (incorrectly) a child of a Text Object, its Para looks like this:
        //      "3.2.1.0-4.2"; notice there is a period after the last dash.
        // [4]  Level 1 Headings paragraphs have no dashes nor periods, and look like "1".
        // [5]  Level 1 Text paragraphs have no periods, but do contain a dash, and look like "0-1".
        // [6]  Objects in tables (cell, row, table) have para numbers like nested text objects: "3-2.0-1.0-1".
        //      This function would correctly identifies them as Text without an explicit check,
        //      but to save time and string table space a specific table-row-cell check is made.
 
        // Strategy: Get the paragraph 'number' of the object.  
        //      If its in a table its not a Heading
        //      If it contains no dash then its a heading.
        //      If it has a dash but there is a period following the last dash, then its a heading.
        //      Otherwise (there's a dash with no period after it) then its not a Heading object.
 
        if (null obj)           return(false)
 
        int     LocDash, LocDashLast, LocDashNext, LocPeriod    // Locations in the paragraph number
 
        if (fIsTable (obj))     return(false)   // Table-Row-Cell objects are not Headings.
 
        gl_bufIsHeadNum = number(obj)   // Put the Paragraph in the buffer
        LocDash         = contains(gl_bufIsHeadNum, '-', 0)
        if (LocDash < 0)     return(true)    // No dashes at all, must be a heading (e.g. "2" or "3.2.1")
 
                                // Find the last dash in the Paragraph Number
        LocDashLast = LocDash
        LocDashNext = LocDash
        while (LocDashNext > 0)
        {  LocDashNext        = contains(gl_bufIsHeadNum, '-', LocDashLast+1)
           // print "\t" tempStringOf(gl_bufIsHeadNum) "\t" LocDash "\t" LocDashNext "\n"
           if (LocDashNext > 0) LocDashLast = LocDashNext
        }
                                // Find any period after the last dash:
        LocPeriod       = contains(gl_bufIsHeadNum, '.', LocDashLast+1)
//      print "fIsHead\t" (stringOf(gl_bufIsHeadNum)) "\t" LocDash "\t" LocPeriod "\n"
        return(LocPeriod > LocDashLast)      // Its a heading if there's a period after the last Dash
}     // end fIsHead()
 
Object oHead = obj
while (!null oHead and !fIsHead(oHead))
{  oHead = parent(oHead)
}
if (null oHead) //
then obj.attrDXLName = "NoHeading"
else obj.attrDXLName = number(oHead)



fIsHead() is rather complicated, but far less complicated than attempting to determine all the rules DOORS uses to determine whether an object is a "Heading" or not; checking for non-null Object Heading is the standard method and works most of the time, but fails in obscure situations. The rules, BTW, have changed a couple times. Looking at the paragraph number itself to determine if DOORS thinks its a heading (and display in the main column) seems to be working; if someone finds an exception I'd like to hear about it.

-Louie



 

Re: Getting parent header number
kbmurphy - Tue Jan 10 19:48:45 EST 2012

llandale - Tue Jan 10 15:44:52 EST 2012

WARNING: I just realized the attr-DXL solution below fails to take into account:

  • You do not want to display the paragraph number of a deleted object, and
  • You do not want to display the paragraph number for a deleted object, and
  • It fails when the module is currently displaying deleted objects, since the "number" perm displays differently and you cannot turn on and off that display in an attr-DXL or layout.


Seems then perhaps you need an on-demand script that keeps some target attr value up to date, or some wierd post-module-open trigger that does this work. Or open modules loading the standard view and use the attr-dxl below.

 

Use Attr-DXL.

It seems the requirement is that it will display the object's own paragraph number if its a heading, or if its a text object then display the nearest ancestor's Heading. Thus in your example, the attr-DXL will display "3.1" for both your objects. Perhaps the name of the target attribute is "dxlSectionNumber".

This may work. The key function works well but I'm scribbling the rest.

 

 

//**************************
bool    fIsTable(Object obj)
{     // Is the object part of a DOORS table?
        if (null obj) return(false)
 
        return (row(obj) or table(obj) or cell(obj))
}     // end fIsTable()
 
                // Following buffer is local to fIsHead()
Buffer  gl_bufIsHeadNum = create(32)
//***********************
bool    fIsHead(Object obj)
{     // Does DOORS consider the object a 'Heading' object?
        // Heading Objects are those that display the paragraph number in the Main column in DOORS
        // DOORS considerations for determining 'Heading Objects':
        //      [a] Objects that contain some non-null rich Heading text.
        //      [b] Objects that have no non-null rich Object Text and no Pictures (i.e. empty objects)
        //      [c] However, Cell, row header, and table header objects are NEVER headings,
        //          even when they contain Object Heading or are empty.
        //      [d] Note that invisible rich-text markup only is considered.
        // This function looks at the Paragraph number of the object to determine if
        //      DOORS considers it a Heading.
 
        // Notes:
        // [1]  Normal Text Object Paragraph Numbers have a ZeroDash and look like this: "3.2.1.0-4".
        //      Notice there is a dash after the last period.
        // [2]  Normal Headings Numbers have no dash and look like: "3.2.1".
        // [3]  When a Heading is (incorrectly) a child of a Text Object, its Para looks like this:
        //      "3.2.1.0-4.2"; notice there is a period after the last dash.
        // [4]  Level 1 Headings paragraphs have no dashes nor periods, and look like "1".
        // [5]  Level 1 Text paragraphs have no periods, but do contain a dash, and look like "0-1".
        // [6]  Objects in tables (cell, row, table) have para numbers like nested text objects: "3-2.0-1.0-1".
        //      This function would correctly identifies them as Text without an explicit check,
        //      but to save time and string table space a specific table-row-cell check is made.
 
        // Strategy: Get the paragraph 'number' of the object.  
        //      If its in a table its not a Heading
        //      If it contains no dash then its a heading.
        //      If it has a dash but there is a period following the last dash, then its a heading.
        //      Otherwise (there's a dash with no period after it) then its not a Heading object.
 
        if (null obj)           return(false)
 
        int     LocDash, LocDashLast, LocDashNext, LocPeriod    // Locations in the paragraph number
 
        if (fIsTable (obj))     return(false)   // Table-Row-Cell objects are not Headings.
 
        gl_bufIsHeadNum = number(obj)   // Put the Paragraph in the buffer
        LocDash         = contains(gl_bufIsHeadNum, '-', 0)
        if (LocDash < 0)     return(true)    // No dashes at all, must be a heading (e.g. "2" or "3.2.1")
 
                                // Find the last dash in the Paragraph Number
        LocDashLast = LocDash
        LocDashNext = LocDash
        while (LocDashNext > 0)
        {  LocDashNext        = contains(gl_bufIsHeadNum, '-', LocDashLast+1)
           // print "\t" tempStringOf(gl_bufIsHeadNum) "\t" LocDash "\t" LocDashNext "\n"
           if (LocDashNext > 0) LocDashLast = LocDashNext
        }
                                // Find any period after the last dash:
        LocPeriod       = contains(gl_bufIsHeadNum, '.', LocDashLast+1)
//      print "fIsHead\t" (stringOf(gl_bufIsHeadNum)) "\t" LocDash "\t" LocPeriod "\n"
        return(LocPeriod > LocDashLast)      // Its a heading if there's a period after the last Dash
}     // end fIsHead()
 
Object oHead = obj
while (!null oHead and !fIsHead(oHead))
{  oHead = parent(oHead)
}
if (null oHead) //
then obj.attrDXLName = "NoHeading"
else obj.attrDXLName = number(oHead)



fIsHead() is rather complicated, but far less complicated than attempting to determine all the rules DOORS uses to determine whether an object is a "Heading" or not; checking for non-null Object Heading is the standard method and works most of the time, but fails in obscure situations. The rules, BTW, have changed a couple times. Looking at the paragraph number itself to determine if DOORS thinks its a heading (and display in the main column) seems to be working; if someone finds an exception I'd like to hear about it.

-Louie



 

Louie--mind giving an example of one of those obscure situations?

Re: Getting parent header number
llandale - Wed Jan 11 14:01:55 EST 2012

kbmurphy - Tue Jan 10 19:48:45 EST 2012
Louie--mind giving an example of one of those obscure situations?

  • Newly created objects have no Heading Text yet are considered "Headings" by DOORS. These are those with no raw Heading Text, no raw Object Text, no rich Heading/Object text, no OLE diagram (of course), and no Picture.
  • Perhaps no longer, but in v7 of DOORS an otherwise empty object "should" be a heading, but isn't if the text included only invisible rich-text markup; such as if you type a word, make it bold, go back and edit and delete the word. <-- don't think it does this now
  • at some point getting <obj."Object Heading"> (and Text) was inadequate, you had to also check <richText(obj."Object Heading") (and Text).
  • table/row/cell objects are never headings even if they contain Heading Text
  • v6 had an obscure rule that v7 bypassed, so code had to be sensitive to the current DOORS version.

Cannot find it now, but it seems to me I wrote a big sarcastic bewildering paragraph of all the yeah's and neah's and the exceptions and exceptions to the exceptions -- at least all the ones I could think of -- and ended suggesting to "forget that" and to parse the paragraph number itself to determine if its a heading, as fIsHead() does.

This is a sound approach, determining if it is a heading rather than trying to determine if it should be a heading. And surely its far faster then retrieving text/string attr values.

I will concede however that the desire to determine if DOORS says its a heading may be inadequate; if you want to display the "section text" with a text object you may very well look up until you find any Object Heading. That is, you really want a "Spec Heading" not a "DOORS Heading", in the rare cases they are not the same.

-Louie

Re: Getting parent header number
SystemAdmin - Wed Jan 11 17:04:45 EST 2012

I use an algorithm like

  • N
  • N.N
  • N.N.N and so forth


are pure headers, where N is a valid integer and N.N.N represents number(someObject).

Generally the "find header of" algorithm is:

 

  • start at the top and drill down to the desired object.
  • the last level without a "-" is it's parent heading.
  • if no "-" is encountered, the selected object is a heading.

 

 

void skSplitOn(char ch, Buffer b, Skip skParaLevels) {
    bool hasMoreThanOne = false;
    int indexOfSeparator = -1;
    int firstSep = -1;
    int lastSep = 0;
    int counter = 0;
    while (lastSep != length(b)) {
        if (null(b)) break;
        if (length(stringOf(b)) == 0) break;
        if (length(b) < 2) break;
        int startingIndex = firstSep + 1;
        lastSep = contains(b, ch, startingIndex)
        if (lastSep == -1) {
            lastSep = length(b);
        }
        string myParamStr = b[ startingIndex : (lastSep-1) ];
        if (length(myParamStr) > 0) {
            counter++;
            bool putValue = put(skParaLevels, counter, myParamStr);
        }
        firstSep = lastSep;
    } // while
}
 
bool strContains(string str, char ch) {
    int i;
    bool found = false;
    for (i = 0; i < length(str); i++) {
        if (str[i] == ch) {
            found = true;
            break;
        }
    }
    return found;
}
 
 
void headingParaNumberOf(Object o){
    Buffer b = create();
    Skip skParaLevels = create();
    b += number(o);
    skSplitOn('.', b, skParaLevels)
    setempty(b);
    print("The paragraph " (number(o) ));
    string strLevel;
    bool first = true;
    bool pureHeading = true;
    string headingStr = "";
    for strLevel in skParaLevels do {
        if (strContains(strLevel, '-')) {
            pureHeading = false;
            break;
        }
        if (!first) {
            b += ".";
        }
        b += strLevel;
        first = false;
    }
    if (pureHeading) {
        print(" is a heading\n");
    } else {
        print(" has heading " stringOf(b) "\n");
    }
    delete(b);
    delete(skParaLevels);
}
 
 
headingParaNumberOf((Object current));

 


Sample runs:
A normal heading:

The paragraph 2.1.2.1.1.1 is a heading

Regular text object under a heading:

The paragraph 2.1.2.1.2.2.0-1 has heading 2.1.2.1.2.2

A table cell under a heading:

The paragraph 2.1.2.1.3.3.0-2.0-1.0-3.0-3 has heading 2.1.2.1.3.3

CONFESSION:
I have never seen the following from Louie's post

 

 

 

 

 

 

// [3]  When a Heading is (incorrectly) a child of a Text Object, its Para looks like this:
//      "3.2.1.0-4.2"; notice there is a period after the last dash.

 


Louie, how do you get one of these (or is it a relic of the past)?

 

 

 

 

 

Re: Getting parent header number
llandale - Wed Jan 11 18:20:14 EST 2012

SystemAdmin - Wed Jan 11 17:04:45 EST 2012

I use an algorithm like

  • N
  • N.N
  • N.N.N and so forth


are pure headers, where N is a valid integer and N.N.N represents number(someObject).

Generally the "find header of" algorithm is:

 

  • start at the top and drill down to the desired object.
  • the last level without a "-" is it's parent heading.
  • if no "-" is encountered, the selected object is a heading.

 

 

void skSplitOn(char ch, Buffer b, Skip skParaLevels) {
    bool hasMoreThanOne = false;
    int indexOfSeparator = -1;
    int firstSep = -1;
    int lastSep = 0;
    int counter = 0;
    while (lastSep != length(b)) {
        if (null(b)) break;
        if (length(stringOf(b)) == 0) break;
        if (length(b) < 2) break;
        int startingIndex = firstSep + 1;
        lastSep = contains(b, ch, startingIndex)
        if (lastSep == -1) {
            lastSep = length(b);
        }
        string myParamStr = b[ startingIndex : (lastSep-1) ];
        if (length(myParamStr) > 0) {
            counter++;
            bool putValue = put(skParaLevels, counter, myParamStr);
        }
        firstSep = lastSep;
    } // while
}
 
bool strContains(string str, char ch) {
    int i;
    bool found = false;
    for (i = 0; i < length(str); i++) {
        if (str[i] == ch) {
            found = true;
            break;
        }
    }
    return found;
}
 
 
void headingParaNumberOf(Object o){
    Buffer b = create();
    Skip skParaLevels = create();
    b += number(o);
    skSplitOn('.', b, skParaLevels)
    setempty(b);
    print("The paragraph " (number(o) ));
    string strLevel;
    bool first = true;
    bool pureHeading = true;
    string headingStr = "";
    for strLevel in skParaLevels do {
        if (strContains(strLevel, '-')) {
            pureHeading = false;
            break;
        }
        if (!first) {
            b += ".";
        }
        b += strLevel;
        first = false;
    }
    if (pureHeading) {
        print(" is a heading\n");
    } else {
        print(" has heading " stringOf(b) "\n");
    }
    delete(b);
    delete(skParaLevels);
}
 
 
headingParaNumberOf((Object current));

 


Sample runs:
A normal heading:

The paragraph 2.1.2.1.1.1 is a heading

Regular text object under a heading:

The paragraph 2.1.2.1.2.2.0-1 has heading 2.1.2.1.2.2

A table cell under a heading:

The paragraph 2.1.2.1.3.3.0-2.0-1.0-3.0-3 has heading 2.1.2.1.3.3

CONFESSION:
I have never seen the following from Louie's post

 

 

 

 

 

 

// [3]  When a Heading is (incorrectly) a child of a Text Object, its Para looks like this:
//      "3.2.1.0-4.2"; notice there is a period after the last dash.

 


Louie, how do you get one of these (or is it a relic of the past)?

 

 

 

 

 

Create Object A, then Object B below A, then C below B. Add Heading to A and C, add text to B. Display level and number and the main Heading/Text column. If A has paragraph "3.2" then B is "3.2.0-1" and C is "3.2.0-1.1" and note how bad the main column looks.

Notice heading C is a child of text object B, something that doesn't make sense in the paper specification world; and I cannot think of any other situation where it would be appropriate.

Now create text object D below C and run your code on D. It finds A not C because B is not pure. Seems to me that the parent section of D is still C, even though C is placed incorrectly in the module (likely it "should" be below A not B).

My code works if you really want to find the object that DOORS displays as its heading; but your desire to find the "pure" heading may have a lot of value, IMO so long as you add the notion of looking for actual Heading Text as well. Perhaps a "pretty" heading is one with a "pure" paragraph number and some Heading to display. In fact, I think my one report script will do just that now that you've brought it to my attention.

  • Louie

The above situation prompted me to write my "checkHierarchy.dxl" script, that checks for the above and several other non-spec-like structures..
  • .. like a text object having a previous sibling heading object. Insert Heading object E below C pushing D further down, and note D's particularly ugly paragraph number. D "appears" to be in E's section but it's not.

I've got a "fGetTitle(Object)" function that gets the heading text and paragraph number of the parent object, but adds looking for Object Short Text as well. This is real handy when making reports for folks who deal only with paper specs.

Re: Getting parent header number
bmohamed - Wed Oct 29 12:32:01 EDT 2014

llandale - Tue Jan 10 15:44:52 EST 2012

WARNING: I just realized the attr-DXL solution below fails to take into account:

  • You do not want to display the paragraph number of a deleted object, and
  • You do not want to display the paragraph number for a deleted object, and
  • It fails when the module is currently displaying deleted objects, since the "number" perm displays differently and you cannot turn on and off that display in an attr-DXL or layout.


Seems then perhaps you need an on-demand script that keeps some target attr value up to date, or some wierd post-module-open trigger that does this work. Or open modules loading the standard view and use the attr-dxl below.

 

Use Attr-DXL.

It seems the requirement is that it will display the object's own paragraph number if its a heading, or if its a text object then display the nearest ancestor's Heading. Thus in your example, the attr-DXL will display "3.1" for both your objects. Perhaps the name of the target attribute is "dxlSectionNumber".

This may work. The key function works well but I'm scribbling the rest.

 

 

//**************************
bool    fIsTable(Object obj)
{     // Is the object part of a DOORS table?
        if (null obj) return(false)
 
        return (row(obj) or table(obj) or cell(obj))
}     // end fIsTable()
 
                // Following buffer is local to fIsHead()
Buffer  gl_bufIsHeadNum = create(32)
//***********************
bool    fIsHead(Object obj)
{     // Does DOORS consider the object a 'Heading' object?
        // Heading Objects are those that display the paragraph number in the Main column in DOORS
        // DOORS considerations for determining 'Heading Objects':
        //      [a] Objects that contain some non-null rich Heading text.
        //      [b] Objects that have no non-null rich Object Text and no Pictures (i.e. empty objects)
        //      [c] However, Cell, row header, and table header objects are NEVER headings,
        //          even when they contain Object Heading or are empty.
        //      [d] Note that invisible rich-text markup only is considered.
        // This function looks at the Paragraph number of the object to determine if
        //      DOORS considers it a Heading.
 
        // Notes:
        // [1]  Normal Text Object Paragraph Numbers have a ZeroDash and look like this: "3.2.1.0-4".
        //      Notice there is a dash after the last period.
        // [2]  Normal Headings Numbers have no dash and look like: "3.2.1".
        // [3]  When a Heading is (incorrectly) a child of a Text Object, its Para looks like this:
        //      "3.2.1.0-4.2"; notice there is a period after the last dash.
        // [4]  Level 1 Headings paragraphs have no dashes nor periods, and look like "1".
        // [5]  Level 1 Text paragraphs have no periods, but do contain a dash, and look like "0-1".
        // [6]  Objects in tables (cell, row, table) have para numbers like nested text objects: "3-2.0-1.0-1".
        //      This function would correctly identifies them as Text without an explicit check,
        //      but to save time and string table space a specific table-row-cell check is made.
 
        // Strategy: Get the paragraph 'number' of the object.  
        //      If its in a table its not a Heading
        //      If it contains no dash then its a heading.
        //      If it has a dash but there is a period following the last dash, then its a heading.
        //      Otherwise (there's a dash with no period after it) then its not a Heading object.
 
        if (null obj)           return(false)
 
        int     LocDash, LocDashLast, LocDashNext, LocPeriod    // Locations in the paragraph number
 
        if (fIsTable (obj))     return(false)   // Table-Row-Cell objects are not Headings.
 
        gl_bufIsHeadNum = number(obj)   // Put the Paragraph in the buffer
        LocDash         = contains(gl_bufIsHeadNum, '-', 0)
        if (LocDash < 0)     return(true)    // No dashes at all, must be a heading (e.g. "2" or "3.2.1")
 
                                // Find the last dash in the Paragraph Number
        LocDashLast = LocDash
        LocDashNext = LocDash
        while (LocDashNext > 0)
        {  LocDashNext        = contains(gl_bufIsHeadNum, '-', LocDashLast+1)
           // print "\t" tempStringOf(gl_bufIsHeadNum) "\t" LocDash "\t" LocDashNext "\n"
           if (LocDashNext > 0) LocDashLast = LocDashNext
        }
                                // Find any period after the last dash:
        LocPeriod       = contains(gl_bufIsHeadNum, '.', LocDashLast+1)
//      print "fIsHead\t" (stringOf(gl_bufIsHeadNum)) "\t" LocDash "\t" LocPeriod "\n"
        return(LocPeriod > LocDashLast)      // Its a heading if there's a period after the last Dash
}     // end fIsHead()
 
Object oHead = obj
while (!null oHead and !fIsHead(oHead))
{  oHead = parent(oHead)
}
if (null oHead) //
then obj.attrDXLName = "NoHeading"
else obj.attrDXLName = number(oHead)



fIsHead() is rather complicated, but far less complicated than attempting to determine all the rules DOORS uses to determine whether an object is a "Heading" or not; checking for non-null Object Heading is the standard method and works most of the time, but fails in obscure situations. The rules, BTW, have changed a couple times. Looking at the paragraph number itself to determine if DOORS thinks its a heading (and display in the main column) seems to be working; if someone finds an exception I'd like to hear about it.

-Louie



 

What could be added to this to display the heading number and heading eg...'3.2.2.2 Data Collection'?

Thanks. 

Re: Getting parent header number
Mike.Scharnow - Wed Oct 29 13:01:57 EDT 2014

bmohamed - Wed Oct 29 12:32:01 EDT 2014

What could be added to this to display the heading number and heading eg...'3.2.2.2 Data Collection'?

Thanks. 

that should just be:

 

Line 72: else obj.attrDXLName = number (oHead) ": "  oHead."Object Heading" ""

 

Re: Getting parent header number
bmohamed - Wed Oct 29 13:25:58 EDT 2014

Mike.Scharnow - Wed Oct 29 13:01:57 EDT 2014

that should just be:

 

Line 72: else obj.attrDXLName = number (oHead) ": "  oHead."Object Heading" ""

 

Perfect.  Thanks.